package top.healthylife.gateway.platform.br.api.utils;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.expression.ExpressionException;
import top.healthylife.basics.utils.exception.BusinessException;
import top.healthylife.basics.utils.utils.RedisUtil;
import top.healthylife.basics.utils.utils.SpringContextUtil;
import top.healthylife.basics.utils.utils.SystemThreadLocal;
import top.healthylife.basics.utils.utils.TenantContextHolder;
import top.healthylife.gateway.core.util.SystemConfigUtils;
import top.healthylife.gateway.dto.request.req.ApiReqDto;
import top.healthylife.gateway.platform.br.api.config.SystemConfig;
import top.healthylife.gateway.platform.br.api.external.dto.BrModulePo;
import top.healthylife.gateway.platform.br.api.external.po.AccountPo;
import top.healthylife.gateway.platform.br.api.mapper.BrModulePoMapper;
import top.heathylife.gateway.common.cons.SystemCons;
import top.heathylife.gateway.common.po.RecordRequestInfo;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;


/**
 * @author GaoZexi
 * @describe 调用获取token的标准工具, 1小时失效
 * @datetime 2019/12/19 17:13
 */
@Slf4j
public class GetmeltingTokenUtils {

    final static MerchantServer MS = new MerchantServer();
    final static String TOKENID = "tokenid";

    final static String SUCCESSCODE = "00";

    final static String NOHIT = "100002";

    final static String CODE = "code";

    static final Map<String, String> CODE_MAP = new HashMap<>();

    static {
        //用户策略配置的规则集、反欺诈风险识别、信用风险识别至少有一个返回成功
        CODE_MAP.put("00", "操作成功");
        //明确失败，联系客服
        CODE_MAP.put("100001", "程序错误");
        //	查询key值未命中数据库
        CODE_MAP.put("100002", "匹配结果为空");
        //	请补充key值（id/cell/name）,或检查必传key值是否合法
        CODE_MAP.put("100003", "必选key值缺失或不合法");
        //	请联系客服查询正确用户名
        CODE_MAP.put("100004", "商户不存在或用户名错误");
        //	请联系客服找回密码
        CODE_MAP.put("100005", "登陆密码不正确");
        //	明确失败，联系客服
        CODE_MAP.put("100006", "请求参数格式错误");
        //	请重新登录
        CODE_MAP.put("100007", "Tokenid过期");
        //	Apicode 错误
        CODE_MAP.put("100008", "apiCode账户标识错误");
        //	请联系客服添加IP白名单
        CODE_MAP.put("100009", "IP白名单未添加");
        //	测试客户查询限制为50条/天
        CODE_MAP.put("100010", "超出当天访问次数");
        //	明确失败
        CODE_MAP.put("100011", "账户停用");
        CODE_MAP.put("100012", "请求套餐为空或产品权限未添加");
        //	重新传入策略编号
        CODE_MAP.put("100013", "apicode未配置此策略");
        //、风险识别未开通权限	联系客服
        CODE_MAP.put("100014", "策略依赖的产品未开通权限");
        //	联系客服，反馈出错场景
        CODE_MAP.put("1000015", "checkCode错误");
        //，无法解析的错误	修改请求json或联系客服
        CODE_MAP.put("1000016", "捕获请求json异常");
        //	调整并发数量或联系客服
        CODE_MAP.put("1000017", "并发过大");
        //	客户重新请求
        CODE_MAP.put("200000", "内部延迟");
        //	联系客服
        CODE_MAP.put("200001", "规则引擎异常");
        //	联系客服
        CODE_MAP.put("200006", "数据打平错误");
    }


    public static MerchantServer getMs() {
        return MS;
    }

    /**
     * 获取token
     *
     * @param userName 商户用户名
     * @param password 商户密码
     * @param apiCode  商户标识
     * @return
     */
    public static String getTokenId(String userName, String password, String apiCode) {
        int i = 0;
        return getToken(userName, password, apiCode, i);
    }

    private static String getToken(String userName, String password, String apiCode, int i) {
        String token = SysConstant.redisKey("token", userName + password + apiCode);
        //查看token是否存在
        if (RedisUtil.exists(token)) {
            return (String) RedisUtil.get(token);
        }

        //重新登录
        try {
            String loginResult = MS.login(userName, password, "LoginApi", apiCode);
            JSONObject jsonObject = JSONObject.parseObject(loginResult);
            //有效期一个小时，一个小时没有调用接口就会过期。需要重新登录。
            String code = jsonObject.getString(CODE);
            if (!SUCCESSCODE.equals(code)) {
                if (CODE_MAP.containsKey(code)) {
                    throw new BusinessException("百融登录错误,apiCode:{},错误信息:{}", apiCode, CODE_MAP.get(code));
                }
                throw new BusinessException("百融登录错误,apiCode:{},错误码:{}", apiCode, code);
            }
            String tokenid = jsonObject.getString(TOKENID);
            if (tokenid == null) {
                throw new BusinessException("百融登录未知异常,apiCode:{}", apiCode);
            }
            RedisUtil.set(token, tokenid, 1, TimeUnit.HOURS);
            return tokenid;
        } catch (Exception e) {
            throw new BusinessException(e.getMessage());
        }
        //if (i <= 3) {
        //    //重试
        //    return getToken(userName, password, apiCode, ++i);
        //}

    }

    /**
     * 通过api标识自动处理请求接口
     *
     * @param apiIdentification 接口标识
     * @param requestData       请求的数据
     * @return
     */
    public static String getApiData(String apiIdentification, String version, Map<String, Object> requestData, SystemConfig systemConfig) {
        //根据产品名称,查询数据库(策略id)
        BrModulePoMapper brModulePoMapper = SpringContextUtil.getBean(BrModulePoMapper.class);
        List<BrModulePo> strategyBos;
        if (version == null) {
            strategyBos = brModulePoMapper.selectList(new QueryWrapper<BrModulePo>().lambda()
                    .eq(BrModulePo::getBrModule, apiIdentification)
                    .isNull(BrModulePo::getBrVersion));
        } else {
            strategyBos = brModulePoMapper.selectList(new QueryWrapper<BrModulePo>().lambda()
                    .eq(BrModulePo::getBrModule, apiIdentification)
                    .eq(BrModulePo::getBrVersion, version));
        }
        //判断是否包含这个configId
        String tenantId = TenantContextHolder.getTenantId();
        log.info("当前租户id:{}", tenantId);
        Map<String, BrModulePo> poMap = strategyBos.stream().collect(Collectors.toMap(BrModulePo::getConfigId, Function.identity()));
        //
        String brModuleConfigId = String.join(",", poMap.keySet());
        log.info("brModuleConfigId:{}", brModuleConfigId);
        //获取用户的百融账户列表
        String sysConfigId = systemConfig.getAccount().stream().map(AccountPo::getConfigId).collect(Collectors.joining(","));
        //sysConfigId = 用户配置的百融账户列表的configId集合
        log.info("sysConfigId:{}", sysConfigId);
        //抛出此异常是因为
        AccountPo accountPo = systemConfig.getAccount().stream().filter(e -> poMap.containsKey(e.getConfigId()))
                .findAny().orElseThrow(() -> new ExpressionException("未查询到百融策略配置信息,请联系管理员修改br_module"));
        log.info("最终使用的账号apiCode:{},config:{}", accountPo.getApicode(), accountPo.getConfigId());
        BrModulePo modulePo = poMap.get(accountPo.getConfigId());
        //获取系统信息,获取用户名
        String userName = accountPo.getUserName();
        String password = accountPo.getPassWord();
        String apiCode = accountPo.getApicode();
        //获取token,固定这样写
        Object tokenId = GetmeltingTokenUtils.getTokenId(userName, password, apiCode);
        JSONObject jsonObject = new JSONObject();

        requestData.put("strategy_id", modulePo.getStrategyId());
        requestData.put("conf_id", modulePo.getStrategyId());

        //这是一个接口固定值
        jsonObject.put("reqData", requestData);
        jsonObject.put("apiName", modulePo.getApiname());
        jsonObject.put(TOKENID, tokenId);
        //记录请求信息
        RecordRequestInfo.recordRequest("使用百融sdk访问,无法直接获取", jsonObject.toJSONString(), null, "post");
        //直接请求接口数据
        String apiData = GetmeltingTokenUtils.getMs().getApiData(jsonObject.toString(), apiCode);
        //将三方元数据存储到线程变量
        SystemThreadLocal.set(SystemCons.ORIGIN_DATA, apiData);
        JSONObject res = JSONObject.parseObject(apiData);
        if (res == null) {
            log.error(modulePo.getProductName() + "请求百融接口失败,响应={}", apiData);
            throw new BusinessException("请求百融接口失败,响应:" + apiData);
        }
        String code = res.getString(CODE);
        if (SUCCESSCODE.equals(code) || NOHIT.equals(code)) {
            log.info(modulePo.getProductName() + "请求百融接口成功,响应={}", apiData);
            return apiData;
        }
        throw new BusinessException("请求百融接口失败,响应:" + CODE_MAP.get(code));

    }


    /**
     * 访问日志输出和配置获取
     *
     * @param brDemoApi
     * @return
     */
    public static SystemConfig systemConfigInfo(ApiReqDto brDemoApi) {
        //可直接通过工具类获取这个对象
        SystemConfig o = (SystemConfig) SystemConfigUtils.get();
        log.info("收到请求参数为:{}", JSONObject.toJSONString(brDemoApi));
        log.info("收到请求参数为:{}", JSONObject.toJSONString(o));
        return o;
    }
}
